﻿using System.ComponentModel;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace BootstrapBlazor.Components;

/// <summary>
/// 条件输出控制组件
/// </summary>
public class ConditionRender : BootstrapComponentBase
{
    /// <summary>
    /// 获得/设置 ConditionRender 名字 此名字通过 <see cref="OnQueryCondition"/> 第一个参数传递给使用者
    /// </summary>
    [Parameter]
    public string? Name { get; set; }

    /// <summary>
    /// 获得/设置 此 ConditionRender 需要观察的对象
    /// </summary>
    [Parameter]
    public INotifyPropertyChanged? Watch { get; set; }

    /// <summary>
    /// 获得/设置 此 ConditionRender 需要观察的属性
    /// </summary>
    [Parameter]
    public Expression<Func<INotifyPropertyChanged>>[]? WatchProperties { get; set; }

    /// <summary>
    /// 获得/设置 是否显示此 ConditionRender 默认显示 返回 true 时显示
    /// </summary>
    [Parameter]
    public Func<string?, Task<bool>>? OnQueryCondition { get; set; }

    /// <summary>
    /// 获得/设置 是否显示此 ConditionRender 默认显示 null 未参与判断 设置 true 时显示
    /// </summary>
    [Parameter]
    public bool? Condition { get; set; }

    /// <summary>
    /// 获得/设置 子组件内容
    /// </summary>
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    /// <summary>
    /// 获得/设置 条件通过时子组件内容
    /// </summary>
    [Parameter]
    public RenderFragment? TrueContent { get; set; }

    /// <summary>
    /// 获得/设置 条件不通过时子组件内容
    /// </summary>
    [Parameter]
    public RenderFragment? FalseContent { get; set; }

    private bool IsShow { get; set; } = true;

    protected override async Task OnParametersSetAsync()
    {
        await base.OnParametersSetAsync();

        if (Condition.HasValue)
        {
            IsShow = Condition.Value;
        }
        else if (OnQueryCondition != null)
        {
            IsShow = await OnQueryCondition(Name);
        }
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        if (Watch != null)
        {
            Watch.PropertyChanged += OnWatchPropertyChanged;
        }
    }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        if (IsShow)
        {
            builder.AddContent(0, TrueContent ?? ChildContent);
        }
        else
        {
            builder.AddContent(0, FalseContent);
        }
    }

    protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (Watch != null)
            {
                Watch.PropertyChanged -= OnWatchPropertyChanged;
            }
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private async void ExecuteCondition()
    {
        if (Condition.HasValue)
        {
            IsShow = Condition.Value;
        }
        else if (OnQueryCondition != null)
        {
            IsShow = await OnQueryCondition(Name);
        }
    }

    private void OnWatchPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        var isChanged = false;
        if (e != null && !string.IsNullOrWhiteSpace(e.PropertyName))
        {
            if (WatchProperties != null)
            {
                foreach (var item in WatchProperties)
                {
                    if (e.PropertyName == ParseAccessor(item))
                    {
                        isChanged = true;
                        break;
                    }
                }
            }
            else
            {
                isChanged = true;
            }
        }

        if (isChanged)
        {
            ExecuteCondition();
            StateHasChanged();
        }
    }

    private static string? ParseAccessor<T>(Expression<Func<T>> accessor)
    {
        if (accessor.Body == null)
        {
            return null;
        }

        var expression = accessor.Body;
        if (expression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert && unaryExpression.Type == typeof(object))
        {
            expression = unaryExpression.Operand;
        }

        if (expression is not MemberExpression memberExpression)
        {
            return null;
        }

        return memberExpression.Member.Name;
    }
}
